`include "defines.svh"

module StageMA (
    input   logic                           clock,
    input   logic                           reset,

    // StageMA -> StageEX
    output  logic                           ma2ex_allow,

    // StageEX -> StageMA
    input   logic                           ex2ma_valid,
    input   logic [`EX2MA_BUS_WD-1:0]       ex2ma_bus,

    // StageMA -> StageWB
    output  logic                           ma2wb_valid,
    output  logic [`MA2WB_BUS_WD-1:0]       ma2wb_bus,

    // StageWB -> StageMA
    input   logic                           wb2ma_allow,

    // CSR -> StageEX
    input   logic                           csr_taken,

    // StageMA -> StageID @ Forward & Block
    output  logic [`MA_RF_HZD_BUS_WD-1:0]   ma_rf_hzd_bus,
    output  logic [`MA_CSR_BLK_BUS_WD-1:0]  ma_csr_blk_bus,

    // StageMA -> StageIF/EX @ Cancel Flag
    output  logic                           ma_cancel,

    /* ********* DataBus Interface ********* */
    input   logic [63:0]                    dbus_rdata,
    input   logic                           dbus_data_ok,
    input   logic                           dbus_load_fault,
    input   logic                           dbus_store_fault
    /* ********* DataBus Interface ********* */
);

    logic           ma_ready;
    logic           ma_valid;

/* ************************* Pipeline ************************* */
/* -------------- StageEX <=> StageMA -------------- */

    // for debug signal
    logic           inst_mem;
    logic           skip_ref;
    logic           icache_miss;
    logic           dcache_miss;
    // to StageMA signal
    logic [63:0]    ma_pc;
    logic [31:0]    ma_inst;
    logic [63:0]    ma_next_pc;
    /* verilator lint_off UNUSEDSIGNAL */
    logic [15:0]    ex2ma_excp_flags;
    logic [15:0]    ex2ma_intr_flags;
    logic [3:0]     mem_op;
    logic [31:0]    mem_addr;
    logic           load_sext;
    logic           mem_has_req;
    logic [2:0]     rf_sel;
    logic [63:0]    alu_result;
    logic [63:0]    csr_rdata;
    // to Regfile signal
    logic           ma_rf_we;
    logic [4:0]     ma_rf_waddr;
    // to CSR signal
    logic           inst_mret;
    logic           ma_csr_we;
    logic [11:0]    ma_csr_waddr;
    logic [63:0]    ma_csr_wdata;

    logic [`EX2MA_BUS_WD-1:0]   ex2ma_bus_R;

    assign {
        // for debug
        inst_mem,
        skip_ref,
        icache_miss,
        dcache_miss,
        // to StageMA
        ma_pc,
        ma_inst,
        ma_next_pc,
        ex2ma_excp_flags,
        ex2ma_intr_flags,
        mem_op,
        mem_addr,
        load_sext,
        mem_has_req,
        rf_sel,
        alu_result,
        csr_rdata,
        // to Regfile
        ma_rf_we,
        ma_rf_waddr,
        // to CSR
        inst_mret,
        ma_csr_we,
        ma_csr_waddr,
        ma_csr_wdata
    } = ex2ma_bus_R;

    assign  ma_ready = (mem_has_req & dbus_data_ok) | (~mem_has_req);
    assign  ma2ex_allow = (~ma_valid) | (ma_ready & wb2ma_allow);
    assign  ma2wb_valid = ma_valid & ma_ready;

    always_ff @ (posedge clock) begin
        if (reset | csr_taken) begin
            ma_valid <= 1'b0;
        end
        else if (ma2ex_allow) begin
            ma_valid <= ex2ma_valid;
        end
    end

    always_ff @ (posedge clock) begin
        if (reset) begin
            ex2ma_bus_R <= `EX2MA_BUS_WD'h0;
        end
        else if (ex2ma_valid & ma2ex_allow) begin
            ex2ma_bus_R <= ex2ma_bus;
        end
    end
/* -------------- StageEX <=> StageMA -------------- */

/* -------------- StageMA <=> StageWB -------------- */
    assign ma2wb_bus = {
        // for debug
        inst_mem,
        skip_ref,
        icache_miss,
        dcache_miss,
        // to StageWB
        ma_pc,
        ma_inst,
        ma_next_pc,
        ma_excp_flags,
        ma_intr_flags,
        // to Regfile
        ma_rf_we,
        ma_rf_waddr,
        ma_rf_wdata,
        // to CSR
        inst_mret,
        ma_csr_we,
        ma_csr_waddr,
        ma_csr_wdata
    };
/* -------------- StageMA <=> StageWB -------------- */
/* ************************* Pipeline ************************* */


/* ************************* MEM ************************* */
    wire lb_op  = mem_op[0];
    wire lh_op  = mem_op[1];
    wire lw_op  = mem_op[2];
    wire ld_op  = mem_op[3];

    wire [63:0] rdata   = dbus_rdata;
    wire [7:0]  lb_data = rdata[{mem_addr[2:0], 3'b0} +: 8 ];
    wire [15:0] lh_data = rdata[{mem_addr[2:1], 4'b0} +: 16];
    wire [31:0] lw_data = rdata[{mem_addr[2]  , 5'b0} +: 32];
    wire [63:0] ld_data = rdata;

    wire [63:0] load_data = {64{lb_op}} & { {56{load_sext & lb_data[ 7]}}, lb_data }
                          | {64{lh_op}} & { {48{load_sext & lh_data[15]}}, lh_data }
                          | {64{lw_op}} & { {32{load_sext & lw_data[31]}}, lw_data }
                          | {64{ld_op}} &                                 ld_data ;
/* ************************* MEM ************************* */


/* ************************* CSR ************************* */
    // CSR Block
    wire ma_csr_blk_flag = ma_valid & ma_csr_we;
    assign ma_csr_blk_bus = {
        ma_csr_blk_flag,
        ma_csr_waddr
    };
/* ************************* CSR ************************* */


/* ******************************** RegFile ******************************** */
    // RegFile Write Data
    wire [63:0] ma_rf_wdata = {64{rf_sel[0]}} & alu_result
                            | {64{rf_sel[1]}} & load_data
                            | {64{rf_sel[2]}} & csr_rdata  ;
    // RegFile Block & Forward
    wire        ma_rf_blk_flag = (ma_valid & ~ma_ready) & ma_rf_we & (|ma_rf_waddr);
    wire        ma_rf_fwd_flag = (ma_valid &  ma_ready) & ma_rf_we & (|ma_rf_waddr);
    wire [63:0] ma_rf_fwd_data  = ma_rf_wdata;

    assign ma_rf_hzd_bus = {
        ma_rf_blk_flag,
        ma_rf_fwd_flag,
        ma_rf_waddr,
        ma_rf_fwd_data
    };
/* ******************************** RegFile ******************************** */


/* ************************************* Trap ************************************* */
    wire has_other_excp = (|ex2ma_excp_flags[4:0])
                        | ( ex2ma_excp_flags[6])
                        | (|ex2ma_excp_flags[15:8]);
    logic [15:0] ma_excp_flags;
    assign ma_excp_flags[4:0] = ex2ma_excp_flags[4:0];
    assign ma_excp_flags[5]   = ma_valid & (~has_other_excp) & dbus_load_fault;
    assign ma_excp_flags[6]   = ex2ma_excp_flags[6];
    assign ma_excp_flags[7]   = ma_valid & (~has_other_excp) & dbus_store_fault;
    assign ma_excp_flags[15:8]= ex2ma_excp_flags[15:8];

    wire [15:0] ma_intr_flags = ex2ma_intr_flags;

/* ----------------------------- Cancel Flag ----------------------------- */
    assign ma_cancel = ma_valid & ((|ma_intr_flags) | (|ma_excp_flags) | inst_mret);
/* ----------------------------- Cancel Flag ----------------------------- */
/* ************************************* Trap ************************************* */

endmodule
